From: Berk Cakar <berk2238@hotmail.com>
Date: Thu, 24 Jun 2021 00:01:23 +0300
Subject: [PATCH] Added PiSi support back

---
 backends/pisi/meson.build       |  21 ++
 backends/pisi/pisiBackend.py    | 632 ++++++++++++++++++++++++++++++++
 backends/pisi/pk-backend-pisi.c | 369 +++++++++++++++++++
 meson_options.txt               |   2 +-
 4 files changed, 1023 insertions(+), 1 deletion(-)
 create mode 100644 backends/pisi/meson.build
 create mode 100644 backends/pisi/pisiBackend.py
 create mode 100644 backends/pisi/pk-backend-pisi.c

diff --git a/backends/pisi/meson.build b/backends/pisi/meson.build
new file mode 100644
index 000000000..71236ad11
--- /dev/null
+++ b/backends/pisi/meson.build
@@ -0,0 +1,21 @@
+shared_module(
+  'pk_backend_pisi',
+  'pk-backend-pisi.c',
+  include_directories: packagekit_src_include,
+  dependencies: [
+    packagekit_glib2_dep,
+    gmodule_dep,
+  ],
+  c_args: [
+    '-DPK_COMPILATION',
+    '-DG_LOG_DOMAIN="PackageKit-Pisi"',
+  ],
+  install: true,
+  install_dir: pk_plugin_dir,
+)
+
+install_data(
+  'pisiBackend.py',
+  install_dir: join_paths(get_option('datadir'), 'PackageKit', 'helpers', 'pisi'),
+  install_mode: 'rwxr--r--',
+)
diff --git a/backends/pisi/pisiBackend.py b/backends/pisi/pisiBackend.py
new file mode 100644
index 000000000..f9c19eeaf
--- /dev/null
+++ b/backends/pisi/pisiBackend.py
@@ -0,0 +1,632 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Licensed under the GNU General Public License Version 2
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Copyright (C) 2007 S.Çağlar Onur <caglar@pardus.org.tr>
+# Copyright (C) 2013 Ikey Doherty <ikey@solusos.com>
+# Copyright (C) 2021 Berk Çakar <berk2238@hotmail.com>
+
+# Notes to PiSi based distribution maintainers
+# /etc/PackageKit/pisi.conf must contain a mapping of PiSi component to
+# PackageKit groups for correct operation, i.e.
+#   system.utils       = system
+#   desktop.gnome      = desktop-gnome
+# If you have a BTS you must also provide Bug-Regex and Bug-URI fields, i.e:
+#   Bug-Regex = Bug-SolusOS: T(\d+)
+#   Bug-URI = http://inf.solusos.com/T%s
+# We use simple python string formatting to replace the %s with the first
+# matched group in the regular expression. So in the example above, we expect
+# to see "Bug-SolusOS: T9" for example, on its own line in a package update
+# comment.
+
+import pisi
+import pisi.ui
+from packagekit.backend import *
+from packagekit.package import PackagekitPackage
+from packagekit import enums
+import os.path
+import piksemel
+import re
+
+class SimplePisiHandler(pisi.ui.UI):
+
+    def __init(self):
+        pisi.ui.UI.__init__(self, False, False)
+
+    def display_progress(self, **ka):
+        self.the_callback(**ka)
+
+
+class PackageKitPisiBackend(PackageKitBaseBackend, PackagekitPackage):
+
+    SETTINGS_FILE = "/etc/PackageKit/pisi.conf"
+
+    def __init__(self, args):
+        self.bug_regex = None
+        self.bug_uri = None
+        self._load_settings()
+        PackageKitBaseBackend.__init__(self, args)
+
+        self.componentdb = pisi.db.componentdb.ComponentDB()
+        # self.filesdb = pisi.db.filesdb.FilesDB()
+        self.installdb = pisi.db.installdb.InstallDB()
+        self.packagedb = pisi.db.packagedb.PackageDB()
+        self.repodb = pisi.db.repodb.RepoDB()
+
+        # Do not ask any question to users
+        self.options = pisi.config.Options()
+        self.options.yes_all = True
+
+        self.saved_ui = pisi.context.ui
+
+    def _load_settings(self):
+        """ Load the PK Group-> PiSi component mapping """
+        if os.path.exists(self.SETTINGS_FILE):
+            with open(self.SETTINGS_FILE, "r") as mapping:
+                self.groups = {}
+                for line in mapping.readlines():
+                    line = line.replace("\r", "").replace("\n", "").strip()
+                    if line.strip() == "" or "#" in line:
+                        continue
+
+                    splits = line.split("=")
+                    key = splits[0].strip()
+                    value = splits[1].strip()
+
+                    # Check if this contains our bug keys
+                    if key == "Bug-Regex":
+                        self.bug_regex = re.compile(value)
+                        continue
+                    if key == "Bug-URI":
+                        self.bug_uri = value
+                        continue
+                    self.groups[key] = value
+        else:
+            self.groups = {}
+
+    def __get_package_version(self, package):
+        """ Returns version string of given package """
+        # Internal FIXME: PiSi may provide this
+        if package.build is not None:
+            version = "%s-%s-%s" % (package.version, package.release,
+                                    package.build)
+        else:
+            version = "%s-%s" % (package.version, package.release)
+        return version
+
+    def __get_package(self, package, filters=None):
+        """ Returns package object suitable for other methods """
+        if self.installdb.has_package(package):
+            status = INFO_INSTALLED
+            pkg = self.installdb.get_package(package)
+        elif self.packagedb.has_package(package):
+            status = INFO_AVAILABLE
+            pkg = self.packagedb.get_package(package)
+        else:
+            self.error(ERROR_PACKAGE_NOT_FOUND, "Package was not found")
+
+        if filters:
+            if "none" not in filters:
+                if FILTER_INSTALLED in filters and status != INFO_INSTALLED:
+                    return
+                if FILTER_NOT_INSTALLED in filters and status == INFO_INSTALLED:
+                    return
+                if FILTER_GUI in filters and "app:gui" not in pkg.isA:
+                    return
+                if FILTER_NOT_GUI in filters and "app:gui" in pkg.isA:
+                    return
+
+        version = self.__get_package_version(pkg)
+
+        id = self.get_package_id(pkg.name, version, pkg.architecture, "")
+
+        return self.package(id, status, pkg.summary)
+
+    def depends_on(self, filters, package_ids, recursive):
+        """ Prints a list of depends for a given package """
+        self.allow_cancel(True)
+        self.percentage(None)
+
+        package = self.get_package_from_id(package_ids[0])[0]
+
+        for pkg in self.packagedb.get_package(package).runtimeDependencies():
+            # FIXME: PiSi API has really inconsistent for return types
+            # and arguments!
+            self.__get_package(pkg.package)
+
+    def get_details(self, package_ids):
+        """ Prints a detailed description for a given package """
+        self.allow_cancel(True)
+        self.percentage(None)
+
+        package = self.get_package_from_id(package_ids[0])[0]
+
+        if self.packagedb.has_package(package):
+            pkg = self.packagedb.get_package(package)
+            repo = self.packagedb.get_package_repo(pkg.name, None)
+            pkg_id = self.get_package_id(pkg.name,
+                                         self.__get_package_version(pkg),
+                                         pkg.architecture, repo[1])
+
+            if pkg.partOf in self.groups:
+                group = self.groups[pkg.partOf]
+            else:
+                group = GROUP_UNKNOWN
+
+            homepage = pkg.source.homepage if pkg.source.homepage is not None\
+                else ''
+
+            self.details(pkg_id, '', ",".join(pkg.license), group, pkg.description,
+                         homepage, pkg.packageSize)
+        else:
+            self.error(ERROR_PACKAGE_NOT_FOUND, "Package was not found")
+
+    def get_files(self, package_ids):
+        """ Prints a file list for a given package """
+        self.allow_cancel(True)
+        self.percentage(None)
+
+        package = self.get_package_from_id(package_ids[0])[0]
+
+        if self.installdb.has_package(package):
+            pkg = self.packagedb.get_package(package)
+            repo = self.packagedb.get_package_repo(pkg.name, None)
+            pkg_id = self.get_package_id(pkg.name,
+                                         self.__get_package_version(pkg),
+                                         pkg.architecture, repo[1])
+
+            pkg = self.installdb.get_files(package)
+
+            files = map(lambda y: "/%s" % y.path, pkg.list)
+
+            file_list = ";".join(files)
+            self.files(pkg_id, file_list)
+
+    def get_repo_list(self, filters):
+        """ Prints available repositories """
+        self.allow_cancel(True)
+        self.percentage(None)
+
+        for repo in pisi.api.list_repos():
+            # Internal FIXME: What an ugly way to get repo uri
+            # FIXME: Use repository enabled/disabled state
+            uri = self.repodb.get_repo(repo).indexuri.get_uri()
+            self.repo_detail(repo, uri, True)
+
+    def required_by(self, filters, package_ids, recursive):
+        """ Prints a list of requires for a given package """
+        self.allow_cancel(True)
+        self.percentage(None)
+
+        package = self.get_package_from_id(package_ids[0])[0]
+
+        # FIXME: Handle packages which is not installed from repository
+        for pkg in self.packagedb.get_rev_deps(package):
+            self.__get_package(pkg[0])
+
+    def get_updates(self, filter):
+        """ Prints available updates and types """
+        self.allow_cancel(True)
+        self.percentage(None)
+
+        self._updates = dict()
+        for package in pisi.api.list_upgradable():
+            pkg = self.packagedb.get_package(package)
+            version = self.__get_package_version(pkg)
+            id = self.get_package_id(pkg.name, version, pkg.architecture, "")
+            installed_package = self.installdb.get_package(package)
+
+            repo = self.packagedb.get_package_repo(pkg.name, None)[1]
+            pindex = "/var/lib/pisi/index/%s/pisi-index.xml" % repo
+
+            self._updates[pkg.name] = \
+                self._extract_update_details(pindex, pkg.name)
+            bug_uri = self._updates[pkg.name][3]
+
+            # FIXME: PiSi must provide this information as a single API call :(
+            updates = [i for i in self.packagedb.get_package(package).history
+                       if pisi.version.Version(i.release) >
+                       installed_package.release]
+            if pisi.util.any(lambda i: i.type == "security", updates):
+                self.package(id, INFO_SECURITY, pkg.summary)
+            elif bug_uri != "":
+                self.package(id, INFO_BUGFIX, pkg.summary)
+            else:
+                self.package(id, INFO_NORMAL, pkg.summary)
+
+    def _extract_update_details(self, pindex, package_name):
+        document = piksemel.parse(pindex)
+        packages = document.tags("Package")
+        for pkg in packages:
+            if pkg.getTagData("Name") == package_name:
+                history = pkg.getTag("History")
+                update = history.tags("Update")
+                update_message = "Updated"
+                update_release = 0
+                update_date = ""
+                needsReboot = False
+                bugURI = ""
+                for update in update:
+                    if int(update.getAttribute("release")) > update_release:
+                        update_release = int(update.getAttribute("release"))
+                        # updater = update.getTagData("Name")
+                        update_message = update.getTagData("Comment")
+                        update_message = update_message.replace("\n", ";")
+                        update_date = update.getTagData("Date")
+                        needsReboot = False
+                        try:
+                            requires = update.getTag("Requires")
+                            action = requires.getTagData("Action")
+                            if action == "systemRestart":
+                                needsReboot = True
+                        except Exception:
+                            pass
+                # Determine if this is a bug fix
+                for line in update_message.split(";"):
+                    m = self.bug_regex.match(line)
+                    if m is not None:
+                        bugURI = self.bug_uri % m.group(1)
+                        break
+                return (update_message, update_date, needsReboot, bugURI)
+            pkg = pkg.nextTag("Package")
+        return("Log not found", "", False, "")
+
+    def get_update_detail(self, package_ids):
+        for package_id in package_ids:
+            package = self.get_package_from_id(package_id)[0]
+            the_package = self.installdb.get_package(package)
+            updates = [package_id]
+            obsoletes = ""
+            # TODO: Add regex matching for #FIXES:ID or something similar
+            cve_url = ""
+            package_url = the_package.source.homepage
+            vendor_url = package_url if package_url is not None else ""
+            issued = ""
+
+            changelog = ""
+            # TODO: Set to security_issued if security update
+            issued = updated = ""
+            update_message, security_issued, needsReboot, bugURI = \
+                self._updates[package]
+
+            # TODO: Add tagging to repo's, or a mapping file
+            state = UPDATE_STATE_STABLE
+            reboot = "system" if needsReboot else "none"
+
+            self.update_detail(package_id, updates, obsoletes, vendor_url,
+                               bugURI, cve_url, reboot, update_message,
+                               changelog, state, issued, updated)
+
+    def download_packages(self, directory, package_ids):
+        """ Download the given packages to a directory """
+        self.allow_cancel(False)
+        self.percentage(None)
+        self.status(STATUS_DOWNLOAD)
+
+        packages = list()
+
+        def progress_cb(**kw):
+            self.percentage(int(kw['percent']))
+
+        ui = SimplePisiHandler()
+        for package_id in package_ids:
+            package = self.get_package_from_id(package_id)[0]
+            packages.append(package)
+            try:
+                pkg = self.packagedb.get_package(package)
+            except:
+                self.error(ERROR_PACKAGE_NOT_FOUND, "Package was not found")
+        try:
+            pisi.api.set_userinterface(ui)
+            ui.the_callback = progress_cb
+            if directory is None:
+                directory = os.path.curdir
+            pisi.api.fetch(packages, directory)
+            # Scan for package
+            for package in packages:
+                package_obj = self.packagedb.get_package(package)
+                uri = package_obj.packageURI.split("/")[-1]
+                location = os.path.join(directory, uri)
+                self.files(package_id, location)
+            pisi.api.set_userinterface(self.saved_ui)
+        except Exception, e:
+            self.error(ERROR_PACKAGE_DOWNLOAD_FAILED,
+                       "Could not download package: %s" % e)
+        self.percentage(None)
+
+    def install_files(self, only_trusted, files):
+        """ Installs given package into system"""
+
+        # FIXME: use only_trusted
+
+        # FIXME: install progress
+        self.allow_cancel(False)
+        self.percentage(None)
+
+        def progress_cb(**kw):
+            self.percentage(int(kw['percent']))
+
+        ui = SimplePisiHandler()
+
+        self.status(STATUS_INSTALL)
+        pisi.api.set_userinterface(ui)
+        ui.the_callback = progress_cb
+
+        try:
+            self.status(STATUS_INSTALL)
+            pisi.api.install(files)
+        except pisi.Error, e:
+            # FIXME: Error: internal-error : Package re-install declined
+            # Force needed?
+            self.error(ERROR_PACKAGE_ALREADY_INSTALLED, e)
+        pisi.api.set_userinterface(self.saved_ui)
+
+    def _report_all_for_package(self, package, remove=False):
+        """ Report all deps for the given package """
+        if not remove:
+            deps = self.packagedb.get_package(package).runtimeDependencies()
+            # TODO: Add support to report conflicting packages requiring removal
+            #conflicts = self.packagedb.get_package (package).conflicts
+            for dep in deps:
+                if not self.installdb.has_package(dep.name()):
+                    dep_pkg = self.packagedb.get_package(dep.name())
+                    repo = self.packagedb.get_package_repo(dep_pkg.name, None)
+                    version = self.__get_package_version(dep_pkg)
+                    pkg_id = self.get_package_id(dep_pkg.name, version,
+                                                 dep_pkg.architecture, repo[1])
+                    self.package(pkg_id, INFO_INSTALLING, dep_pkg.summary)
+        else:
+            rev_deps = self.installdb.get_rev_deps(package)
+            for rev_dep, depinfo in rev_deps:
+                if self.installdb.has_package(rev_dep):
+                    dep_pkg = self.packagedb.get_package(rev_dep)
+                    repo = self.packagedb.get_package_repo(dep_pkg.name, None)
+                    version = self.__get_package_version(dep_pkg)
+                    pkg_id = self.get_package_id(dep_pkg.name, version,
+                                                 dep_pkg.architecture, repo[1])
+                    self.package(pkg_id, INFO_REMOVING, dep_pkg.summary)
+
+    def install_packages(self, transaction_flags, package_ids):
+        """ Installs given package into system"""
+        # FIXME: fetch/install progress
+        self.allow_cancel(False)
+        self.percentage(None)
+
+        packages = list()
+
+        # FIXME: use only_trusted
+        for package_id in package_ids:
+            package = self.get_package_from_id(package_id)[0]
+            if self.installdb.has_package(package):
+                self.error(ERROR_PACKAGE_NOT_INSTALLED,
+                           "Package is already installed")
+            packages.append(package)
+
+        def progress_cb(**kw):
+            self.percentage(int(kw['percent']))
+
+        ui = SimplePisiHandler()
+
+        self.status(STATUS_INSTALL)
+        pisi.api.set_userinterface(ui)
+        ui.the_callback = progress_cb
+
+        if TRANSACTION_FLAG_SIMULATE in transaction_flags:
+            # Simulated, not real.
+            for package in packages:
+                self._report_all_for_package(package)
+            return
+        try:
+            pisi.api.install(packages)
+        except pisi.Error, e:
+            self.error(ERROR_UNKNOWN, e)
+        pisi.api.set_userinterface(self.saved_ui)
+
+    def refresh_cache(self, force):
+        """ Updates repository indexes """
+        # TODO: use force ?
+        self.allow_cancel(False)
+        self.percentage(0)
+        self.status(STATUS_REFRESH_CACHE)
+
+        slice = (100 / len(pisi.api.list_repos())) / 2
+
+        percentage = 0
+        for repo in pisi.api.list_repos():
+            pisi.api.update_repo(repo)
+            percentage += slice
+            self.percentage(percentage)
+
+        self.percentage(100)
+
+    def remove_packages(self, transaction_flags, package_ids,
+                        allowdeps, autoremove):
+        """ Removes given package from system"""
+        self.allow_cancel(False)
+        self.percentage(None)
+        # TODO: use autoremove
+        packages = list()
+
+        for package_id in package_ids:
+            package = self.get_package_from_id(package_id)[0]
+            if not self.installdb.has_package(package):
+                self.error(ERROR_PACKAGE_NOT_INSTALLED,
+                           "Package is not installed")
+            packages.append(package)
+
+        def progress_cb(**kw):
+            self.percentage(int(kw['percent']))
+
+        ui = SimplePisiHandler()
+
+        package = self.get_package_from_id(package_ids[0])[0]
+        self.status(STATUS_REMOVE)
+
+        if TRANSACTION_FLAG_SIMULATE in transaction_flags:
+            # Simulated, not real.
+            for package in packages:
+                self._report_all_for_package(package, remove=True)
+            return
+        try:
+            pisi.api.remove(packages)
+        except pisi.Error, e:
+            self.error(ERROR_CANNOT_REMOVE_SYSTEM_PACKAGE, e)
+        pisi.api.set_userinterface(self.saved_ui)
+
+    def repo_set_data(self, repo_id, parameter, value):
+        """ Sets a parameter for the repository specified """
+        self.allow_cancel(False)
+        self.percentage(None)
+
+        if parameter == "add-repo":
+            try:
+                pisi.api.add_repo(repo_id, value, parameter)
+            except pisi.Error, e:
+                self.error(ERROR_UNKNOWN, e)
+
+            try:
+                pisi.api.update_repo(repo_id)
+            except pisi.fetcher.FetchError:
+                pisi.api.remove_repo(repo_id)
+                err = "Could not reach the repository, removing from system"
+                self.error(ERROR_REPO_NOT_FOUND, err)
+        elif parameter == "remove-repo":
+            try:
+                pisi.api.remove_repo(repo_id)
+            except pisi.Error:
+                self.error(ERROR_REPO_NOT_FOUND, "Repository does not exist")
+        else:
+            self.error(ERROR_NOT_SUPPORTED, "Parameter not supported")
+
+    def resolve(self, filters, package):
+        """ Turns a single package name into a package_id
+        suitable for the other methods """
+        self.allow_cancel(True)
+        self.percentage(None)
+
+        self.__get_package(package[0], filters)
+
+    def search_details(self, filters, values):
+        """ Prints a detailed list of packages contains search term """
+        self.allow_cancel(True)
+        self.percentage(None)
+        self.status(STATUS_INFO)
+
+        # Internal FIXME: Use search_details instead of _package when API
+        # gains that ability :)
+        for pkg in pisi.api.search_package(values):
+            self.__get_package(pkg, filters)
+
+    def search_file(self, filters, values):
+        """ Prints the installed package which contains the specified file """
+        self.allow_cancel(True)
+        self.percentage(None)
+        self.status(STATUS_INFO)
+
+        for value in values:
+            # Internal FIXME: Why it is needed?
+            value = value.lstrip("/")
+
+            for pkg, files in pisi.api.search_file(value):
+                self.__get_package(pkg)
+
+    def search_group(self, filters, values):
+        """ Prints a list of packages belongs to searched group """
+        self.allow_cancel(True)
+        self.percentage(None)
+        self.status(STATUS_INFO)
+
+        for value in values:
+            packages = list()
+            for item in self.groups:
+                if self.groups[item] == value:
+                    try:
+                        pkgs = self.componentdb.get_packages(item, walk=False)
+                        packages.extend(pkgs)
+                    except:
+                        self.error(ERROR_GROUP_NOT_FOUND,
+                                   "Component %s was not found" % value)
+            for pkg in packages:
+                self.__get_package(pkg, filters)
+
+    def search_name(self, filters, values):
+        """ Prints a list of packages contains search term in its name """
+        self.allow_cancel(True)
+        self.percentage(None)
+        self.status(STATUS_INFO)
+
+        for value in values:
+            for pkg in pisi.api.search_package([value]):
+                self.__get_package(pkg, filters)
+
+    def update_packages(self, transaction_flags, package_ids):
+        """ Updates given package to its latest version """
+
+        # FIXME: use only_trusted
+
+        # FIXME: fetch/install progress
+        self.allow_cancel(False)
+        self.percentage(None)
+
+        packages = list()
+        for package_id in package_ids:
+            package = self.get_package_from_id(package_id)[0]
+            if not self.installdb.has_package(package):
+                self.error(ERROR_PACKAGE_NOT_INSTALLED,
+                           "Cannot update a package that is not installed")
+            packages.append(package)
+
+        def progress_cb(**kw):
+            self.percentage(int(kw['percent']))
+
+        ui = SimplePisiHandler()
+        pisi.api.set_userinterface(ui)
+        ui.the_callback = progress_cb
+
+        if TRANSACTION_FLAG_SIMULATE in transaction_flags:
+            for package in packages:
+                self._report_all_for_package(package)
+            return
+        try:
+            pisi.api.upgrade(packages)
+        except pisi.Error, e:
+            self.error(ERROR_UNKNOWN, e)
+        pisi.api.set_userinterface(self.saved_ui)
+
+    def update_system(self, only_trusted):
+        """ Updates all available packages """
+        # FIXME: use only_trusted
+        # FIXME: fetch/install progress
+        self.allow_cancel(False)
+        self.percentage(None)
+
+        if not len(pisi.api.list_upgradable()) > 0:
+            self.error(ERROR_NO_PACKAGES_TO_UPDATE, "System is already up2date")
+
+        try:
+            pisi.api.upgrade(pisi.api.list_upgradable())
+        except pisi.Error, e:
+            self.error(ERROR_UNKNOWN, e)
+
+
+def main():
+    backend = PackageKitPisiBackend('')
+    backend.dispatcher(sys.argv[1:])
+
+if __name__ == "__main__":
+    main()
\ No newline at end of file
diff --git a/backends/pisi/pk-backend-pisi.c b/backends/pisi/pk-backend-pisi.c
new file mode 100644
index 000000000..beb2f5836
--- /dev/null
+++ b/backends/pisi/pk-backend-pisi.c
@@ -0,0 +1,369 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 Richard Hughes <richard@hughsie.com>
+ * Copyright (C) 2007 S.Çağlar Onur <caglar@pardus.org.tr>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <pk-backend.h>
+#include <pk-backend-spawn.h>
+
+static PkBackendSpawn *spawn;
+
+void
+pk_backend_start_job (PkBackend *backend, PkBackendJob *job)
+{
+    if (pk_backend_spawn_is_busy (spawn)) {
+        pk_backend_job_error_code (job,
+                       PK_ERROR_ENUM_LOCK_REQUIRED,
+                       "spawned backend requires lock");
+        return;
+    }
+}
+
+void
+pk_backend_initialize (GKeyFile *conf, PkBackend *backend)
+{
+    g_debug ("backend: initialize");
+
+    /* BACKEND MAINTAINER: feel free to remove this when you've
+     * added support for ONLY_DOWNLOAD and merged the simulate
+     * methods as specified in backends/PORTING.txt */
+    /*g_error ("Backend needs to be ported to 0.8.x -- "
+         "see backends/PORTING.txt for details"); */
+
+    spawn = pk_backend_spawn_new (conf);
+    pk_backend_spawn_set_name (spawn, "pisi");
+}
+
+void
+pk_backend_destroy (PkBackend *backend)
+{
+    g_debug ("backend: destroy");
+    g_object_unref (spawn);
+}
+
+PkBitfield
+pk_backend_get_groups (PkBackend *backend)
+{
+    return pk_bitfield_from_enums (
+        PK_GROUP_ENUM_ACCESSORIES,
+        PK_GROUP_ENUM_EDUCATION,
+        PK_GROUP_ENUM_GAMES,
+        PK_GROUP_ENUM_INTERNET,
+        PK_GROUP_ENUM_OTHER,
+        PK_GROUP_ENUM_PROGRAMMING,
+        PK_GROUP_ENUM_MULTIMEDIA,
+        PK_GROUP_ENUM_SYSTEM,
+        PK_GROUP_ENUM_DESKTOP_GNOME,
+        PK_GROUP_ENUM_DESKTOP_KDE,
+        PK_GROUP_ENUM_DESKTOP_OTHER,
+        PK_GROUP_ENUM_PUBLISHING,
+        PK_GROUP_ENUM_SERVERS,
+        PK_GROUP_ENUM_FONTS,
+        PK_GROUP_ENUM_ADMIN_TOOLS,
+        PK_GROUP_ENUM_LOCALIZATION,
+        PK_GROUP_ENUM_VIRTUALIZATION,
+        PK_GROUP_ENUM_SECURITY,
+        PK_GROUP_ENUM_POWER_MANAGEMENT,
+        PK_GROUP_ENUM_UNKNOWN,
+        -1);
+}
+
+PkBitfield
+pk_backend_get_filters (PkBackend *backend)
+{
+    return pk_bitfield_from_enums(
+        PK_FILTER_ENUM_GUI,
+        PK_FILTER_ENUM_INSTALLED,
+        -1);
+}
+
+void
+pk_backend_cancel (PkBackend *backend, PkBackendJob *job)
+{
+    /* this feels bad... */
+    pk_backend_spawn_kill (spawn);
+}
+
+void
+pk_backend_download_packages (PkBackend *backend, PkBackendJob *job, gchar **package_ids, const gchar *directory)
+{
+    gchar *package_ids_temp;
+
+    /* send the complete list as stdin */
+    package_ids_temp = pk_package_ids_to_string (package_ids);
+    pk_backend_spawn_helper (spawn, job, "pisiBackend.py", "download-packages", directory, package_ids_temp, NULL);
+    g_free (package_ids_temp);
+}
+
+void
+pk_backend_depends_on (PkBackend *backend, PkBackendJob *job, PkBitfield filters, gchar **package_ids, gboolean recursive)
+{
+    gchar *filters_text;
+    gchar *package_ids_temp;
+    package_ids_temp = pk_package_ids_to_string (package_ids);
+    filters_text = pk_filter_bitfield_to_string (filters);
+    pk_backend_spawn_helper (spawn, job, "pisiBackend.py", "depends-on", filters_text, package_ids_temp, pk_backend_bool_to_string (recursive), NULL);
+    g_free (filters_text);
+    g_free (package_ids_temp);
+}
+
+void
+pk_backend_get_details (PkBackend *backend, PkBackendJob *job, gchar **package_ids)
+{
+    gchar *package_ids_temp;
+    package_ids_temp = pk_package_ids_to_string (package_ids);
+    pk_backend_spawn_helper (spawn, job, "pisiBackend.py", "get-details", package_ids_temp, NULL);
+    g_free (package_ids_temp);
+}
+
+void
+pk_backend_get_files (PkBackend *backend, PkBackendJob *job, gchar **package_ids)
+{
+    gchar *package_ids_temp;
+    package_ids_temp = pk_package_ids_to_string (package_ids);
+    pk_backend_spawn_helper (spawn, job, "pisiBackend.py", "get-files", package_ids_temp, NULL);
+    g_free (package_ids_temp);
+}
+
+void
+pk_backend_required_by (PkBackend *backend, PkBackendJob *job, PkBitfield filters, gchar **package_ids, gboolean recursive)
+{
+    gchar *filters_text;
+    gchar *package_ids_temp;
+    package_ids_temp = pk_package_ids_to_string (package_ids);
+    filters_text = pk_filter_bitfield_to_string (filters);
+    pk_backend_spawn_helper (spawn, job, "pisiBackend.py", "required-by", filters_text, package_ids_temp, pk_backend_bool_to_string (recursive), NULL);
+    g_free (filters_text);
+    g_free (package_ids_temp);
+}
+
+void
+pk_backend_get_updates (PkBackend *backend, PkBackendJob *job, PkBitfield filters)
+{
+    gchar *filters_text;
+    filters_text = pk_filter_bitfield_to_string (filters);
+    pk_backend_spawn_helper (spawn, job, "pisiBackend.py", "get-updates", filters_text, NULL);
+    g_free (filters_text);
+}
+
+void
+pk_backend_get_update_detail (PkBackend *backend, PkBackendJob *job, gchar **package_ids)
+{
+    gchar *package_ids_temp;
+    package_ids_temp = pk_package_ids_to_string (package_ids);
+    pk_backend_spawn_helper (spawn, job, "pisiBackend.py", "get-update-detail", package_ids_temp, NULL);
+    g_free (package_ids_temp);
+}
+
+void
+pk_backend_install_packages (PkBackend *backend, PkBackendJob *job, PkBitfield transaction_flags, gchar **package_ids)
+{
+    gchar *package_ids_temp;
+    gchar *transaction_flags_temp;
+
+    /* check network state */
+    if (!pk_backend_is_online (backend)) {
+        pk_backend_job_error_code (job, PK_ERROR_ENUM_NO_NETWORK, "Cannot install when offline");
+        pk_backend_job_finished (job);
+        return;
+    }
+
+    /* send the complete list as stdin */
+    package_ids_temp = pk_package_ids_to_string (package_ids);
+    transaction_flags_temp = pk_transaction_flag_bitfield_to_string (transaction_flags);
+    
+    pk_backend_spawn_helper (spawn, job, "pisiBackend.py", "install-packages", transaction_flags_temp, package_ids_temp, NULL);
+    g_free (package_ids_temp);
+    g_free (transaction_flags_temp);
+}
+
+void
+pk_backend_install_files (PkBackend *backend, PkBackendJob *job, PkBitfield transaction_flags, gchar **full_paths)
+{
+    gchar *package_ids_temp;
+    gchar *transaction_flags_temp;
+
+    /* send the complete list as stdin */
+    package_ids_temp = g_strjoinv (PK_BACKEND_SPAWN_FILENAME_DELIM, full_paths);
+    transaction_flags_temp = pk_transaction_flag_bitfield_to_string (transaction_flags);
+    
+    pk_backend_spawn_helper (spawn, job, "pisiBackend.py", "install-files", transaction_flags_temp, package_ids_temp, NULL);
+    g_free (package_ids_temp);
+    g_free (transaction_flags_temp);
+}
+
+void
+pk_backend_refresh_cache (PkBackend *backend, PkBackendJob *job, gboolean force)
+{
+    /* check network state */
+    if (!pk_backend_is_online (backend)) {
+        pk_backend_job_error_code (job, PK_ERROR_ENUM_NO_NETWORK, "Cannot refresh cache whilst offline");
+        pk_backend_job_finished (job);
+        return;
+    }
+
+    pk_backend_spawn_helper (spawn, job, "pisiBackend.py", "refresh-cache", pk_backend_bool_to_string (force), NULL);
+}
+
+void
+pk_backend_remove_packages (PkBackend *backend, PkBackendJob *job,
+                PkBitfield transaction_flags,
+                gchar **package_ids,
+                gboolean allow_deps,
+                gboolean autoremove)
+{
+    gchar *package_ids_temp;
+    gchar *transaction_flags_temp;
+
+    /* send the complete list as stdin */
+    package_ids_temp = pk_package_ids_to_string (package_ids);
+    transaction_flags_temp = pk_transaction_flag_bitfield_to_string (transaction_flags);
+    pk_backend_spawn_helper (spawn,
+        job, "pisiBackend.py",
+        "remove-packages",
+        transaction_flags_temp,
+        package_ids_temp,
+        pk_backend_bool_to_string (allow_deps),
+        pk_backend_bool_to_string (autoremove),
+        NULL);
+        
+    g_free (transaction_flags_temp);
+    g_free (package_ids_temp);
+}
+
+void
+pk_backend_search_details (PkBackend *backend, PkBackendJob *job, PkBitfield filters, gchar **values)
+{
+    gchar *filters_text;
+    gchar *search;
+    filters_text = pk_filter_bitfield_to_string (filters);
+    search = g_strjoinv ("&", values);
+    pk_backend_spawn_helper (spawn, job, "pisiBackend.py", "search-details", filters_text, search, NULL);
+    g_free (search);
+    g_free (filters_text);
+}
+
+void
+pk_backend_search_files (PkBackend *backend, PkBackendJob *job, PkBitfield filters, gchar **values)
+{
+    gchar *filters_text;
+    gchar *search;
+    filters_text = pk_filter_bitfield_to_string (filters);
+    search = g_strjoinv ("&", values);
+    pk_backend_spawn_helper (spawn, job, "pisiBackend.py", "search-file", filters_text, search, NULL);
+    g_free (search);
+    g_free (filters_text);
+}
+
+void
+pk_backend_search_groups (PkBackend *backend, PkBackendJob *job, PkBitfield filters, gchar **values)
+{
+    gchar *filters_text;
+    gchar *search;
+    filters_text = pk_filter_bitfield_to_string (filters);
+    search = g_strjoinv ("&", values);
+    pk_backend_spawn_helper (spawn, job, "pisiBackend.py", "search-group", filters_text, search, NULL);
+    g_free (search);
+    g_free (filters_text);
+}
+
+void
+pk_backend_search_names (PkBackend *backend, PkBackendJob *job, PkBitfield filters, gchar **values)
+{
+    gchar *filters_text;
+    gchar *search;
+    filters_text = pk_filter_bitfield_to_string (filters);
+    search = g_strjoinv ("&", values);
+    pk_backend_spawn_helper (spawn, job, "pisiBackend.py", "search-name", filters_text, search, NULL);
+    g_free (search);
+    g_free (filters_text);
+}
+
+void
+pk_backend_update_packages (PkBackend *backend, PkBackendJob *job, PkBitfield transaction_flags, gchar **package_ids)
+{
+    gchar *package_ids_temp;
+    gchar *transaction_flags_temp;
+
+    /* check network state */
+    if (!pk_backend_is_online (backend)) {
+        pk_backend_job_error_code (job, PK_ERROR_ENUM_NO_NETWORK, "Cannot install when offline");
+        pk_backend_job_finished (job);
+        return;
+    }
+
+    /* send the complete list as stdin */
+    package_ids_temp = pk_package_ids_to_string (package_ids);
+    transaction_flags_temp = pk_transaction_flag_bitfield_to_string (transaction_flags);
+    
+    pk_backend_spawn_helper (spawn, job, "pisiBackend.py", "update-packages", transaction_flags_temp, package_ids_temp, NULL);
+    g_free (package_ids_temp);
+    g_free (transaction_flags_temp);
+}
+
+void
+pk_backend_update_system (PkBackend *backend, PkBackendJob *job, PkBitfield transaction_flags)
+{
+    gchar *transaction_flags_temp;
+    transaction_flags_temp = pk_transaction_flag_bitfield_to_string (transaction_flags);
+    
+    pk_backend_spawn_helper (spawn, job, "pisiBackend.py", "update-system", transaction_flags_temp, NULL);
+    
+    g_free (transaction_flags_temp);
+}
+
+void
+pk_backend_resolve (PkBackend *backend, PkBackendJob *job, PkBitfield filters, gchar **package_ids)
+{
+    gchar *filters_text;
+    gchar *package_ids_temp;
+    filters_text = pk_filter_bitfield_to_string (filters);
+    package_ids_temp = pk_package_ids_to_string (package_ids);
+    pk_backend_spawn_helper (spawn, job, "pisiBackend.py", "resolve", filters_text, package_ids_temp, NULL);
+    g_free (filters_text);
+    g_free (package_ids_temp);
+}
+
+void
+pk_backend_get_repo_list (PkBackend *backend, PkBackendJob *job, PkBitfield filters)
+{
+    gchar *filters_text;
+    filters_text = pk_filter_bitfield_to_string (filters);
+    pk_backend_spawn_helper (spawn, job, "pisiBackend.py", "get-repo-list", filters_text, NULL);
+    g_free (filters_text);
+}
+
+void
+pk_backend_repo_set_data (PkBackend *backend, PkBackendJob *job, const gchar *rid, const gchar *parameter, const gchar *value)
+{
+    pk_backend_spawn_helper (spawn, job, "pisiBackend.py", "repo-set-data", rid, parameter, value, NULL);
+}
+
+const gchar *
+pk_backend_get_description (PkBackend *backend)
+{
+    return "PiSi - Packages Installed Successfully as Intended";
+}
+
+const gchar *
+pk_backend_get_author (PkBackend *backend)
+{
+    return "S.Çağlar Onur <caglar@pardus.org.tr>\nIkey Doherty <ikey@solusos.com>\nBerk Çakar <berk2238@hotmail.com>";
+}
diff --git a/meson_options.txt b/meson_options.txt
index 2f3a1dac9..7623dc9c2 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -1,4 +1,4 @@
-option('packaging_backend', type : 'array', choices : ['alpm', 'aptcc', 'dnf', 'dummy', 'entropy', 'poldek', 'portage', 'slack', 'zypp', 'nix'], value : ['dummy'], description : 'The name of the backend to use')
+option('packaging_backend', type : 'array', choices : ['alpm', 'aptcc', 'dnf', 'dummy', 'entropy', 'pisi', 'poldek', 'portage', 'slack', 'zypp', 'nix'], value : ['dummy'], description : 'The name of the backend to use')
 option('dnf_vendor', type : 'combo', choices : ['fedora', 'mageia', 'openmandriva', 'rosa', 'opensuse'], value : 'fedora', description : 'Vendor configuration when using the dnf backend')
 option('systemd', type : 'boolean', value : true, description : 'Use systemd and logind')
 option('systemdsystemunitdir', type : 'string', value : '', description : 'Directory for systemd service files')
-- 
2.24.3 (Apple Git-128)

